﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Core;
using Core.Utility;
using Descriptions_DomainModel.Model;
using System.Xml.Serialization;
using System.IO;
using CommonClasses.Model;
using Eco.ObjectRepresentation;

namespace LathesSupervisorsAssembly
{

    /// <summary>
    /// Управляющая сборка, осуществляющая работу с портальным роботом по виртуальным пинам
    /// </summary>
    /// <remarks>
    /// Для портального робота будут отведены несколько пинов
    /// 1 Платформа
    ///    1.1 Enabled
    ///    1.2 Direction
    ///    1.3 Pulse
    /// 2 Схват 
    ///     пока не определено
    /// 
    /// Контроллеру передаются команды движения: GO(int dx), STOP(), SETSPEED(int newv).
    /// Его задача приехать в назначенную в команде GO точку, при скорости которую
    /// ему будут задавать во время движения. Скорость для реализации Soft-moving не постоянна 
    /// на протяжении движения, изменяется (посылается контроллеру) на таймере данной сборки т.е.
    /// расчитывается в данной управялющей сборке.
    /// </remarks>
    public class PortalRobotSupervisor : IConnection
    {
        #region Private fields

        /// <summary>
        /// Диспетчер подключений
        /// </summary>
        private IConnection connectionDispatcher;


        /// <summary>
        /// Список пинов состояний и их текущих значений
        /// </summary>
        private List<StatePinDefinition> statePins;


        /// <summary>
        /// Список логик обработки сигналов с входных пинов
        /// </summary>
        private List<HandlerLogic> inPinLogic;


        /// <summary>
        /// Список выходных пинов
        /// </summary>
        private IList<int> outPins;

        /// <summary>
        /// Провайдер сервисов ECO
        /// </summary>
        private IEcoServiceProvider serviceProvider;

        #endregion


        /// <summary>
        /// Конструктор управляющей сборки по управлению портальным роботом
        /// </summary>
        /// <param name="serviceProvider">Провайдер сервисов ECO</param>
        /// <param name="nodeGroup">Группа узлов, представляющая потральный робот</param>
        /// <param name="connectionDispatcher">Диспетчер подключений</param>
        /// <param name="configPath">
        /// Путь до файла с описанием логики обработки сигналов с датчиков.
        /// <remarks>
        /// Строка, описывающая логику должна иметь формат:
        /// ((IncrementPin'-'DecrementPin';'StatePinNumber)('|'IncrementPin'-'DecrementPin';'StatePinNumber)*)
        /// <see cref="ANTLR ;)"/>
        /// IncrementPin - номер пина с датчика, по приходу на который 1 значение StatePinNumber увелечивается на 1
        /// DecrementPin - номер пина с датчика, по приходу на который 1 значение StatePinNumber уменьшается на 1
        /// </remarks>
        /// </param>
        public PortalRobotSupervisor(IEcoServiceProvider serviceProvider, NodeGroup nodeGroup, IConnection connectionDispatcher, string configPath)
        {
            if (connectionDispatcher == null)
                throw new NullReferenceException("PortalRobotSupervisor ctor: ConnectionDispatcher cannot be null!");

            this.serviceProvider = serviceProvider;
            this.connectionDispatcher = connectionDispatcher;

            // читаем список логик обработки входящих пинов inPinsLogicString
            XmlSerializer xmlser = new XmlSerializer(typeof(string));
            TextReader reader = new StreamReader(configPath);
            string inPinsLogicString = xmlser.Deserialize(reader) as string;
            reader.Close();

            FillStatePins(nodeGroup);
            FillInPinsLogics(nodeGroup, inPinsLogicString);
            FillOutPins(nodeGroup);

            SubscribeToDispatcher();
        }

        #region Fill List of pins

        /// <summary>
        /// Заполнить список пинов состояний
        /// </summary>
        /// <param name="nodeGroup">описание группы узлов</param>
        private void FillStatePins(NodeGroup nodeGroup)
        {
            statePins = new List<StatePinDefinition>();

            foreach (var node in nodeGroup.Nodes)
                node.DeepWalk("Nodes", (nod) =>
                        statePins.Add(new StatePinDefinition
                        (   serviceProvider, ((Node)nod).StatePinNumber, 0, 
                            ((Node)nod).StateSet.UpperBound,
                            ((Node)nod).StateSet.BottomBound
                        )));
        }

        /// <summary>
        /// Заполнить список логик обработки входных пинов
        /// </summary>
        /// <param name="nodeGroup">описание группы узлов</param>
        /// <param name="logicString">строка логик обработки входных пинов</param>
        private void FillInPinsLogics(NodeGroup nodeGroup, string logicString)
        {
            inPinLogic = new List<HandlerLogic>();
            try
            {
                var logics = logicString.Split('|');
                foreach (var log in logics)
                {
                    var parsed = log.Split(';', '-');
                    var statePin = int.Parse(parsed[2]);
                    inPinLogic.Add(new HandlerLogic(serviceProvider, statePin, int.Parse(parsed[0]),1));
                    inPinLogic.Add(new HandlerLogic(serviceProvider, statePin, int.Parse(parsed[1]), -1));
                }
            }
            catch (Exception ex)
            {
                throw new FormatException("In pins handling logic definition string has invalid format!", ex);
            }
        }


        /// <summary>
        /// Заполнить список выходных пинов
        /// </summary>
        /// <remarks>
        /// Просматриваются все методы данной группы узлов и берутся соответствующие им пины
        /// </remarks>
        /// <param name="nodeGroup">описание группы узлов</param>
        private void FillOutPins(NodeGroup nodeGroup)
        {
            outPins = new List<int>();

            foreach (var node in nodeGroup.Nodes)
                node.DeepWalk("Nodes", (nod) =>
                    ((Node)nod).Methods.ForEach(method => outPins.Add(method.VirtualPinOut)));
        }

        #endregion


        /// <summary>
        /// Подписаться на сигналы от диспетчера подключений
        /// </summary>
        private void SubscribeToDispatcher()
        {
            connectionDispatcher.OnPinStateChanged += new PinStateChangedHandler(connectionDispatcher_OnPinStateChanged);
        }


        #region IConnection Members

        /// <summary>
        /// Событие: состояние пина изменено
        /// </summary>
        public event PinStateChangedHandler OnPinStateChanged;

        /// <summary>
        /// Послать сигнал на пин 
        /// </summary>
        /// <param name="virtualPinOut">виртуальный пин</param>
        /// <param name="signal">сигнал</param>
        public void ChangePinState(int virtualPinOut, int signal)
        {
            //MP9 управляется только логическими сигналами
            if (!((signal == 0) || (signal == 1)))
                return;

            connectionDispatcher.ChangePinState(virtualPinOut, signal);
        }

        /// <summary>
        /// Наш ли это выходной пин
        /// </summary>
        /// <param name="VirtualPin">вирутальный пин</param>
        /// <returns>bool</returns>
        public bool MatchRange(int VirtualPin)
        {
            return outPins.Contains(VirtualPin);
        }

        /// <summary>
        /// Скинуть значения пинов
        /// </summary>
        public void FlushPins()
        {
            // Значения реальных пинов скинут сами подключения
        }

        /// <summary>
        /// Удалить соединение
        /// </summary>
        public void Dispose()
        {
            // это дело не наше
            return;
        }

        #endregion


        /// <summary>
        /// Обработка сигналов с датчиков (обрабатываются только еденицы)
        /// </summary>
        private void connectionDispatcher_OnPinStateChanged(int virtualPin, int newState)
        {
            //нас интересуют только поднятые сигналы
            Console.WriteLine("<AnyCycleRobotSupervisor.PinStateChanged>:  virtulPin = {0}, newState = {1}", virtualPin, newState);
            if (newState != 1)
                return;
            //ищем логику для данного пина
            var logic = inPinLogic.Find(log => log.PinNumber == virtualPin);
            if (logic == null)
                return;
            //ищем описание пина состояния
            var statePin = statePins.Find(spin => spin.PinNumber == logic.StatePin);
            //применяем логику
            statePin.Value = Math.Max(statePin.Min, Math.Min(statePin.Value + logic.Delta, statePin.Max));
            if (OnPinStateChanged != null)
                OnPinStateChanged(logic.StatePin, statePin.Value);
        }
    }
}
